﻿using System;
using System.ComponentModel.Composition;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Web;
using System.Linq;
using SS.Common.ImageResizer;
using SS.Common.Logging;

namespace SS.AspNet.Utilities
{
    [Export(typeof(FileHelper))]
    [PartCreationPolicy(CreationPolicy.Shared)]
    public class FileHelper
    {
        /// <summary>
        /// The logger instance
        /// </summary>
        public static readonly ILogger Logger = LoggerManager.GetLogger(typeof(FileHelper).Name);

        /// <summary>
        /// The method for handling upload a file to server
        /// </summary>
        /// <param name="file">
        /// The file will be uploaded to server
        /// </param>
        /// <param name="dirPath">
        /// The directory which the file will be stored in
        /// </param>
        /// <param name="fileName">
        /// FileName will be created based on DateTime.Now
        /// as binary string and uploaded file extension
        /// </param>
        public bool UploadFile(HttpPostedFileBase file, string dirPath, ref string fileName)
        {
            var result = true;
            try
            {
                var fileInfo = new FileInfo(file.FileName);
                fileName = DateTime.Now.ToBinary() + fileInfo.Extension;
                var fullPath = Path.Combine(dirPath, fileName);
                file.SaveAs(fullPath);
            }
            catch (Exception ex)
            {
                result = false;
                Logger.ErrorFormat("UploadFile: {0}", ex.ToString());
            }
            return result;
        }

        /// <summary>
        /// The method for handling upload a file to server
        /// </summary>
        /// <param name="file">
        /// The file will be uploaded to server
        /// </param>
        /// <param name="storagePath">
        /// The directory which the file will be stored in
        /// </param>
        /// <param name="fileName">
        /// FileName will be created based on DateTime.Now
        /// as binary string and uploaded file extension
        /// </param>
        public bool UploadFile(HttpPostedFile file, string storagePath, ref string fileName)
        {
            var result = true;
            try
            {
                var fileInfo = new FileInfo(file.FileName);
                fileName = DateTime.Now.ToBinary() + fileInfo.Extension;
                var fullPath = Path.Combine(storagePath, fileName);
                file.SaveAs(fullPath);
            }
            catch (Exception ex)
            {
                result = false;
                Logger.ErrorFormat("UploadFile: {0}", ex.ToString());
            }
            return result;
        }

        /// <summary>
        /// The method for handling upload a file to server
        /// </summary>
        /// <param name="file">
        /// The file will be uploaded to server
        /// </param>
        /// <param name="maximumWidth">
        /// Maximum width
        /// </param>
        /// <param name="storagePath">
        /// The directory which the file will be stored in
        /// </param>
        /// <param name="fileName">
        /// fileName will be created based on DateTime.Now
        /// as binary string and uploaded file extension
        /// </param>
        public bool UploadAndResizeFile(HttpPostedFileBase file, int maximumWidth,
                                        string storagePath, ref string fileName)
        {
            var result = true;
            try
            {
                // Get storage upload path
                var fileInfo = new FileInfo(file.FileName);
                fileName = DateTime.Now.ToBinary() + fileInfo.Extension;
                var fullStoragePath = Path.Combine(storagePath, fileName);

                // Get to be uploaded image width
                var sourceImage = Image.FromStream(file.InputStream);
                var uploadImageWidth = sourceImage.Width;

                if(uploadImageWidth > maximumWidth)
                {
                    var fileByteArray = ConvertImageToByteArray(sourceImage, fileInfo.Extension);
                    var imageResizer = new ImageResizer(fileByteArray);
                    imageResizer.Resize(maximumWidth, ImageEncoding.Jpg90);
                    imageResizer.SaveToFile(fullStoragePath);
                    imageResizer.Dispose();
                }
                else
                {
                    file.SaveAs(fullStoragePath);
                }
            }
            catch (Exception ex)
            {
                result = false;
                Logger.ErrorFormat("UploadAndResizeFile: {0}", ex.ToString());
            }
            return result;
        }

        /// <summary>
        /// The method for handling upload a file to server
        /// </summary>
        /// <param name="file">
        /// The file will be uploaded to server
        /// </param>
        /// <param name="maximumWidth">
        /// Maximum width
        /// </param>
        /// <param name="storagePath">
        /// The directory which the file will be stored in
        /// </param>
        /// <param name="fileName">
        /// fileName will be created based on DateTime.Now
        /// as binary string and uploaded file extension
        /// </param>
        public bool UploadAndResizeFile(HttpPostedFile file, int maximumWidth,
                                        string storagePath, ref string fileName)
        {
            var result = true;
            try
            {
                // Get storage upload path
                var fileInfo = new FileInfo(file.FileName);
                fileName = DateTime.Now.ToBinary() + fileInfo.Extension;
                var fullStoragePath = Path.Combine(storagePath, fileName);

                // Get to be uploaded image width
                var sourceImage = Image.FromStream(file.InputStream);
                var uploadImageWidth = sourceImage.Width;

                if (uploadImageWidth > maximumWidth)
                {
                    var fileByteArray = ConvertImageToByteArray(sourceImage, fileInfo.Extension);
                    var imageResizer = new ImageResizer(fileByteArray);
                    imageResizer.Resize(maximumWidth, ImageEncoding.Jpg90);
                    imageResizer.SaveToFile(fullStoragePath);
                    imageResizer.Dispose();
                }
                else
                {
                    file.SaveAs(fullStoragePath);
                }
            }
            catch (Exception ex)
            {
                result = false;
                Logger.ErrorFormat("UploadAndResizeFile: {0}", ex.ToString());
            }
            return result;
        }

        /// <summary>
        /// The method for deleting a file in server
        /// </summary>
        /// <param name="filePath">The file will be deleted in server</param>
        public bool DeleteFile(string filePath)
        {
            var result = true;
            try
            {
                var file = new FileInfo(filePath);
                if (file.Exists)
                {
                    var fileDir = file.Directory;
                    if (fileDir != null)
                    {
                        this.RemoveFileInFolders(fileDir, file.Name);
                    }
                }
            }
            catch (Exception ex)
            {
                result = false;
                Logger.ErrorFormat("DeleteFile: {0}", ex.ToString());
            }
            return result;
        }

        /// <summary>
        /// Remove files with name in folders
        /// </summary>
        /// <param name="fileDir"></param>
        /// <param name="fileDelete"> </param>
        private void RemoveFileInFolders(DirectoryInfo fileDir, string fileDelete)
        {
            if (fileDir != null)
            {
                var subDirs = fileDir.GetDirectories();
                foreach (var directoryInfo in subDirs)
                {
                    this.RemoveFileInFolders(directoryInfo, fileDelete);
                }
                var fileInfo = fileDir.GetFiles().FirstOrDefault(
                        n => fileDelete.Equals(n.Name, StringComparison.OrdinalIgnoreCase));
                if(fileInfo != null)
                {
                    fileInfo.Delete();
                }
            }
        }

        /// <summary>
        /// Convert image to byte array
        /// </summary>
        /// <param name="image">Image instance</param>
        /// <param name="imageExtension">Image extension</param>
        /// <returns>Byte array</returns>
        private static byte[] ConvertImageToByteArray(Image image, string imageExtension)
        {
            byte[] result;
            try
            {
                var ms = new MemoryStream();
                var imageFormat = ConvertImageExtensionToImageFormat(imageExtension);
                image.Save(ms, imageFormat);
                result = ms.ToArray();
            }
            catch (Exception)
            {
                result = new byte[0];
            }
            return result;
        }

        /// <summary>
        /// Convert from image extension string to image format
        /// </summary>
        /// <param name="imageExtension">Image extension string</param>
        /// <returns>Image format</returns>
        public static ImageFormat ConvertImageExtensionToImageFormat(string imageExtension)
        {
            var result = ImageFormat.Jpeg;
            imageExtension = imageExtension.ToLower();

            switch (imageExtension)
            {
                case ".jpeg":
                case ".jpg":
                    result = ImageFormat.Jpeg;
                    break;

                case ".png":
                    result = ImageFormat.Png;
                    break;

                case ".bmp":
                    result = ImageFormat.Bmp;
                    break;

                case ".gif":
                    result = ImageFormat.Gif;
                    break;
            }

            return result;
        }
    }
}